package com.smsc.headend.task.engine.handler.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.smsc.headend.common.enums.DeviceType;
import com.smsc.headend.common.enums.PaymentSwithPreToPostOptGroupEnum;
import com.smsc.headend.common.enums.TemplateType;
import com.smsc.headend.common.enums.errorcode.TaskHandlerErrorCode;
import com.smsc.headend.common.utils.CustomStringUtils;
import com.smsc.headend.module.data.dto.AnsiSubDataItemDTO;
import com.smsc.headend.module.data.dto.DataItemDTO;
import com.smsc.headend.module.data.enums.OperationName;
import com.smsc.headend.module.meterscheme.entity.CaptureObjsStatus;
import com.smsc.headend.module.protocol.DeviceData;
import com.smsc.headend.module.protocol.ansi.AnsiByteBuffer;
import com.smsc.headend.module.protocol.ansi.AnsiData;
import com.smsc.headend.module.protocol.cosem.bo.CosemData;
import com.smsc.headend.module.report.entity.LogDeviceParamResult;
import com.smsc.headend.module.report.entity.LogDeviceParamResultSub;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.AtomicTaskState;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.module.task.enums.SubTaskStatus;
import com.smsc.headend.module.task.enums.TaskType;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskHandlerException;
import com.smsc.headend.task.engine.handler.TaskHandler;
import com.smsc.headend.task.engine.service.CosemXmlService;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.collection.CaptureObjsStatusService;
import com.smsc.headend.task.engine.utils.AnsiUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class ReadProfileCaptureObjectsHandler implements TaskHandler {
    public static final String LP_TEMPLATE_ID = "lpTemplateId";
    public static final String LB_TEMPLATE_ID = "lbTemplateId";
    public static final String DISPLAY_TEMPLATE_ID = "displayTemplateId";
    public static final String SUB_TASK_COMPLETED = "subTaskCompleted";
    @Autowired
    FeignAssetManagementService assetManagementService;
    @Autowired
    CosemXmlService cosemXmlService;
    @Autowired
    TaskDAO taskDAO;
    @Autowired
    CaptureObjsStatusService captureObjsStatusService;

    @Override
    public int handle(Task task, AtomicTask atomicTask, DeviceData deviceData, String actionResult) throws UDISTaskHandlerException {
        if (!(deviceData instanceof CosemData) && !(deviceData instanceof AnsiData)) {
            throw new UDISTaskHandlerException(TaskHandlerErrorCode.DEVICE_DATA_TYPE_NOT_SUPPORT.toString());
        }

        Map<String, Object> param = getParameterMap(task, atomicTask);
        Long lpDataItemId = Long.valueOf(String.valueOf(param.get("dataItemId")));

        List<Long> subDataItemIds = new ArrayList<>();
        List<String> invalidDataItemInfo = new ArrayList<>();
        List<String> dataItemIdsToWeb = new ArrayList<>();
        JSONArray subDataItems = JSONUtil.createArray();
        JSONArray invalidDataItems = JSONUtil.createArray();

        if (deviceData instanceof AnsiData) {
            AnsiData ansiData = (AnsiData) deviceData;
            AtomicTaskState atomicTaskState = taskDAO.hashGetAtomicTaskState(atomicTask.getAtomicTaskNo());
            // 获取LP Setting
            if(atomicTaskState.getData() == null) {
                log.info("get lp setting {}", ansiData.getAnsiByteBuffer());
                AnsiByteBuffer dataBuffer = ansiData.getAnsiByteBuffer();
                dataBuffer.position(3);
                int NBR_BLKS_SET1 = dataBuffer.getUInt16();
                int NBR_BLK_INTS_SET1 = dataBuffer.getUInt16();
                int NBR_CHNS_SET1 = dataBuffer.getUInt8();
                int MAX_INT_TIME_SET1 = dataBuffer.getUInt8();
                HashMap<Object, Object> data = MapUtil.newHashMap();
                data.put("NBR_CHNS_SET1", NBR_CHNS_SET1);
                atomicTaskState.setData(data);

                return 0;
            }

            Integer size = MapUtil.getInt(atomicTaskState.getData(), "NBR_CHNS_SET1");
            List<AnsiSubDataItemDTO> deviceList = AnsiUtil.parseLpDataSource(size, ansiData.getAnsiByteBuffer());
            for (int i = 0; i < deviceList.size(); i++) {
                AnsiSubDataItemDTO dataItem = deviceList.get(i);
                Long subDataItemId = assetManagementService.getAnsiSubDataItemId(task.getMeterId(), dataItem.getLpSourceSelect(), dataItem.getEndBlkRdgSourceSelect());
                if (subDataItemId != null) {
                    subDataItemIds.add(subDataItemId);
                    subDataItems.add(StrUtil.format("{}:{}", subDataItemId, i));
                    dataItemIdsToWeb.add(String.valueOf(subDataItemId));
                } else {
                    log.error("Failed to find the subDataItemId in db, LpSourceSelect:{}, EndBlkRdgSourceSelect:{}", dataItem.getLpSourceSelect(), dataItem.getEndBlkRdgSourceSelect());
                    invalidDataItemInfo.add(String.format("LpSourceSelect:%s, EndBlkRdgSourceSelect:%s", dataItem.getLpSourceSelect(), dataItem.getEndBlkRdgSourceSelect()));
                    invalidDataItems.add(StrUtil.format("{}:{}", dataItem.getLpSourceSelect(), dataItem.getEndBlkRdgSourceSelect()));
                    dataItemIdsToWeb.add(task.getMeterId().toString());
                }
            }
        } else {
            CosemData cosemData = (CosemData) deviceData;
            if (CollUtil.isNotEmpty(cosemData.getChild())) {
                for (int i = 0; i < cosemData.getChild().size(); i++) {
                    CosemData structData = cosemData.getChild().get(i);
                    CosemData classIdData = structData.getChild().get(0);
                    Integer classId = Convert.toInt(cosemXmlService.getData(classIdData.getType(), classIdData.getValue()));
                    CosemData logicalNameData = structData.getChild().get(1);
                    String logicalName = (String) cosemXmlService.getData(logicalNameData.getType(), logicalNameData.getValue());
                    CosemData attributeIndexData = structData.getChild().get(2);
                    Integer attributeId = Convert.toInt(cosemXmlService.getData(attributeIndexData.getType(), attributeIndexData.getValue()));
                    Long subDataItemId = assetManagementService.getSubDataItemIdByObisInfo(task.getMeterId(), CustomStringUtils.parseHexObisCodeToInt(logicalName), classId, attributeId, "ATTR", DeviceType.METER.getId());
                    if (subDataItemId != null) {
                        subDataItemIds.add(subDataItemId);
                        subDataItems.add(StrUtil.format("{}:{}", subDataItemId, i));
                        dataItemIdsToWeb.add(String.valueOf(subDataItemId));
                    } else {
                        log.error("Failed to find the subDataItemId in db, classId:{}, obis:{}, attributeId:{}", classId, logicalName, attributeId);
                        invalidDataItemInfo.add(String.format("classId:%s, obis:%s, attributeId:%s", classId, CustomStringUtils.parseHexObisCodeToInt(logicalName), attributeId));
                        invalidDataItems.add(StrUtil.format("{}:{}:{}", classId, logicalName, attributeId));
                        dataItemIdsToWeb.add(logicalName);
                    }
                }
            }
        }
        Integer src = LogDeviceParamResult.Src.MANUAL.getCode();
        if (StrUtil.equalsAny(task.getTaskType(), TaskType.ScheduleGet_LP_Period_OBJS.name(), TaskType.ScheduleGet_LB_Capture_OBJS.name())) {
            src = LogDeviceParamResult.Src.SCH.getCode();
        }

        if (StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.ScheduleGet_LP_Period_OBJS.name())) {
            Long groupTemplateId = Long.valueOf(String.valueOf(param.get("groupTemplateId"))) ;
            assetManagementService.saveDeviceParamResultReport(task.getMeterId(), DeviceType.METER.getId(), src, groupTemplateId, TemplateType.LOAD_PROFILE.getId(), lpDataItemId, task.getTaskNo(), LogDeviceParamResultSub.ParamName.LP_OBJS.getName(), Convert.toStr(subDataItemIds),
                    CollUtil.isNotEmpty(invalidDataItemInfo)  ? String.format("Failed to find the subDataItemId in db, %s", Convert.toStr(invalidDataItemInfo)) : "");
        } else if(StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.ScheduleGet_LB_Capture_OBJS.name())) {
            Long groupTemplateId = Long.valueOf(String.valueOf(param.get("groupTemplateId"))) ;
            assetManagementService.saveDeviceParamResultReport(task.getMeterId(), DeviceType.METER.getId(), src, groupTemplateId, TemplateType.LOGBOOK.getId(), lpDataItemId, task.getTaskNo(), LogDeviceParamResultSub.ParamName.LP_OBJS.getName(), Convert.toStr(subDataItemIds),
                    CollUtil.isNotEmpty(invalidDataItemInfo)  ? String.format("Failed to find the subDataItemId in db, %s", Convert.toStr(invalidDataItemInfo)) : "");
        }

        if (StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.ScheduleGet_LP_Period_OBJS.name()) || StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.ScheduleGet_Capture_OBJS.name())) {
            // 捕获清单结果入库
            Long lpTemplateId = Long.valueOf(String.valueOf(param.get(LP_TEMPLATE_ID))) ;
            captureObjsStatusService.saveCaptureObjs(task.getMeterId(), lpTemplateId, subDataItems, invalidDataItems, CaptureObjsStatus.Status.Success.getCode());
        }

        if (StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.Action_DeviceParamConfig_Get.name())) {
            Map<String, String> result = MapUtil.newHashMap();
            Long lpTemplateId = MapUtil.getLong(param, LP_TEMPLATE_ID);
            if(lpTemplateId != null) {
                String periodType = assetManagementService.getProfileIntervalType(lpTemplateId);
                result.put("periodType", periodType);
                result.put(LP_TEMPLATE_ID, lpTemplateId.toString());
            } else if (param.containsKey(LB_TEMPLATE_ID)) {
                Long lbTemplateId = MapUtil.getLong(param, LB_TEMPLATE_ID);
                result.put(LB_TEMPLATE_ID, lbTemplateId.toString());
            } else {
                Long displayTemplateId = MapUtil.getLong(param, DISPLAY_TEMPLATE_ID);
                result.put(DISPLAY_TEMPLATE_ID, displayTemplateId.toString());
            }
            result.put("value", JSONUtil.toJsonStr(dataItemIdsToWeb));
            String paramName = getProfileParamName(atomicTask, param);
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), paramName, result, 600L, true);
            if (isSubTaskGroupLastAtomicTask(task, atomicTask)){
                taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), SUB_TASK_COMPLETED, true,600L, true);
            }
        }

        return 1;
    }

    @Override
    public int handleFailure(Task task, AtomicTask atomicTask, String message) {
        log.info("error {}, ", message);
        Map<String, Object> param = getParameterMap(task, atomicTask);
        if (StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.ScheduleGet_LP_Period_OBJS.name()) || StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.ScheduleGet_Capture_OBJS.name())) {
            // 修改任务状态为Processing
            Long lpTemplateId = Long.valueOf(String.valueOf(param.get(LP_TEMPLATE_ID)));
            captureObjsStatusService.saveCaptureObjs(task.getMeterId(), lpTemplateId, null, null, CaptureObjsStatus.Status.Failed.getCode());
        }
        if (StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.Action_DeviceParamConfig_Get.name())) {
            String paramName = getProfileParamName(atomicTask, param);
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), paramName, message, 600L, false);
            if (isSubTaskGroupLastAtomicTask(task, atomicTask)){
                taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), "subTaskCompleted", true,600L, true);
            }
        }
        if (StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.SwitchMeterPaymentPreToPost.name())) {
            Map<String, Object> parameterMap = getParameterMap(task, atomicTask);
            Long taskId = MapUtil.getLong(parameterMap, "taskId");
            assetManagementService.updateStepStatusToPost(taskId, task.getTaskNo(),task.getMeterId(), PaymentSwithPreToPostOptGroupEnum.ON_DEMAND_MONTHLY_BILLING_PROFILE.getOperationGroup().longValue(), SubTaskStatus.Failed.getCode(), null, SystemClock.now() / 1000,
                    String.format("handle %s fail, [%s]", OperationName.forId(atomicTask.getOperationId()).getName(), message), false);
        }
        return -1;
    }

    private String getProfileParamName(AtomicTask atomicTask, Map<String, Object> param) {
        String paramName;
        OperationName operationName = OperationName.forId(atomicTask.getOperationId());
        if (operationName.equals(OperationName.ReadProfileCaptureObjects)) {
            Long lpTemplateId = MapUtil.getLong(param, LP_TEMPLATE_ID);
            if (lpTemplateId != null) {
                paramName = "ReadProfileCaptureObjects_LP";
            } else {
                paramName = "ReadProfileCaptureObjects_LB";
            }
        } else {
            paramName = operationName.getName();
        }


        return paramName;
    }
}
